#include <math.h>   // floor

#include "player.h"
#include "monster.h"

using namespace std;

Player::Player(const std::string& _name, const int& _health, const int& _attack, const int& _defense, const int& _speed) :
    Entity(_name, _health, _attack, _defense, _speed), level(1), experience(0), enemy(NULL)
{
    skills.resize(5);
}

Player::~Player()
{
    for (int i = 1; i < 5; ++i)
    {
        if (skills[i])    // if the Skill* isn't nullpointer, then delete the skill
        {
            delete skills[i];
        }
    }
}

void Player::writeStats() const
{
    // adds level to stat display
    Entity::writeStats();
    cout << "Level: " << getLevel() << endl;
}

void Player::saveStats(std::ostream& outfile) const
{
    // saves the player's stats to a given filestream
    outfile << getName() << endl
        << getID() << " " << getExperience() << endl;
}

void Player::startCombat()
{
    // starts combat, resets skill cooldowns, sets currenthealth to maxhealth, sets currentspeed to speed, clears buffs
    Entity::startCombat();
    for (int i = 1; i < 5; ++i)
    {
        while (skills[i]->onCooldown()) skills[i]->tick();
    }
    classStart();
}

void Player::useSkill(const int& i)
{
    // activates the ability, deals its damage, displays text, ticks cooldown on other abilities
    enemy->takeDamage(skills[i]->getDamage());
    if (skills[i]->getDamage() > 0)
    {
        cout << getName() << " uses " << skills[i]->getName() << ", it deals " << skills[i]->getDamage() << " damage to " << getEnemy()->getName() << "!" << endl;
    } else
    {
        cout << getName() << " uses " << skills[i]->getName() << "!" << endl;
    }
    skills[i]->activate();
    for (int j = 1; j < 5; ++j)
    {
        if (i != j)
        {
            skills[j]->tick();
        }
    }
}

void Player::gainExperience(const int& exp)
{
    // increases experience and levels up if needed
    experience += exp;
    bool lup = false;
    while (level < floor(0.5 + sqrt(0.25 + 2 * experience)))
    {
        levelup();
        lup = true;
    }
    if (lup)
    {
        cout << "You leveled up! You are now level " << getLevel() << "." << endl;
    }
}

void Player::levelup()
{
    // also increases stats depending on class
    ++level;
}

//--------------------

Rogue::Rogue(const std::string& _name) :
    Player(_name, 100, 8, 2, 16)
{
    // initializes abilities
    Player::classID = 2;
    //
    skills[1] = new Skill(this, 0, 1, 0, 0, 0, 0);
    skills[1]->setName("Basic Attack");
    //
    skills[2] = new Skill(this, 2, 1.4, 0, 0, 2, 0);
    skills[2]->makeBuff(3, "");
    skills[2]->setBuff(Speed, 4);
    skills[2]->setName("Swift Strike");
    //
    skills[3] = new Skill(this, 4, 0, 0, 2, 3, 0);
    skills[3]->setName("Backswing");
    //
    skills[4] = new Skill(this, 0, 0, 0, 0, 0, 0);
    skills[4]->makeBuff(3, "");
    skills[4]->setBuff(Defense, 0.3 * getSpeed());
    skills[4]->setName("Avoidance");
}

void Rogue::writeInfo() const
{
    // displays ability information
    cout << "Rogue" << endl
        << "Quick duelist, overcomes its opponent with multiple small attacks." << endl
        << "Abilities:" << endl
        << "1. Basic Attack: deals damage based on attack value, no cooldown" << endl
        << "2. Swift Strike: deals damage based on attack and level, increases speed for 3 turns by 4, cooldown: 2 turns" << endl
        << "3. Backswing: deals damage based on speed and level, cooldown: 4 turns" << endl
        << "4. Avoidance: increases defense based on speed for 3 turns, no cooldown" << endl << endl;
}

void Rogue::useSkill(const int& i)
{
    // additional effects to activating a skill, recalculates bonus defense based on speed (which can be changed by 2nd skill and level ups)
    if (i == 4)
    {
        skills[4]->setBuff(Defense, 0.3 * getSpeed());
    }
    Player::useSkill(i);
}

void Rogue::levelup()
{
    Player::levelup();
    maxHealth += 8;
    attack += 2;
    defense += 1;
    speed += 2;
}

//------------------------------

Mage::Mage(const std::string& _name) :
    Player(_name, 80, 16, 2, 14)
{
    // initializes abilities
    Player::classID = 3;
    //
    skills[1] = new Skill(this, 0, 1, 0, 0, 0, 0);
    skills[1]->setName("Basic Attack");
    //
    skills[2] = new Skill(this, 4, 1.8, 0, 0, 2, 0);
    skills[2]->setName("Fireball");
    //
    skills[3] = new Skill(this, 2, 0.9, 0, 0, 0, 1);
    skills[3]->setName("Lightning Strike");
    //
    skills[4] = new Skill(this, 0, 0, 0, 0, 0, 0);
    skills[4]->makeBuff(-1, "channel_magic");
    skills[4]->setBuff(Attack, 12);
    skills[4]->setBuff(Defense, -2);
    skills[4]->setBuff(Speed, -4);
    skills[4]->setName("Channel Magic");
}

void Mage::writeInfo() const
{
    // displays ability information
    cout << "Mage" << endl
        << "Uses elemental magic to destroy enemies." << endl
        << "Abilities:" << endl
        << "1. Basic Attack: deals damage based on attack, no cooldown" << endl
        << "2. Fireball: deals damage based on attack and level, cooldown: 4 turns" << endl
        << "3. Lightning Strike: deals damage based on attack, ignores enemy defense, cooldown: 2 turns" << endl
        << "4. Channel Magic: increases attack by 12, reduces defense by 2 and reduces speed by 4 until the end of the fight or until used again, no cooldown" << endl << endl;
}

void Mage::levelup()
{
    Player::levelup();
    maxHealth += 8;
    attack += 3;
    speed += 1;
}

//------------------------------

Warrior::Warrior(const std::string& _name) :
    Player(_name, 120, 12, 5, 10)
{
    // initializes abilities
    Player::classID = 1;
    //
    skills[1] = new Skill(this, 0, 1, 0, 0, 0, 0);
    skills[1]->setName("Basic Attack");
    //
    skills[2] = new Skill(this, 4, 1.6, 0, 0, 2, 0);
    skills[2]->setName("Heavy Swing");
    //
    skills[3] = new Skill(this, 2, 0, 4, 0, 3, 0);
    skills[3]->makeBuff(3, "");
    skills[3]->setBuff(Defense, -5);
    skills[3]->setName("Shield Strike");
    //
    skills[4] = new Skill(this, 0, 0, 0, 0, 0, 0);
    skills[4]->makeBuff(3, "");
    skills[4]->setBuff(Defense, 4);
    skills[4]->setName("Raise Shield");
}

void Warrior::writeInfo() const
{
    // displays ability information
    cout << "Warrior" << endl
        << "Strong fighter using huge shields and big armor." << endl
        << "Abilities:" << endl
        << "1. Basic Attack: deals damage based on attack, increases the damage of the next attack by 10%, no cooldown" << endl
        << "2. Heavy Swing: deals damage based on attack and level, cooldown: 4 turns" << endl
        << "3. Shield Strike: deals damage based on defense and level, reduces defense by 5 for 3 turns, cooldown: 2 turns" << endl
        << "4. Raise Shield: increases defense by 4 for 3 turns, no cooldown" << endl << endl;
}

void Warrior::useSkill(const int& i)
{
    // checks for the 10% bonus damage modifier, applies it if needed, sets its flag for the next attack
    double bonus = 0;
    if (hasbonus)
    {
        bonus = (skills[i]->getDamage() * 0.1);
    }
    Player::useSkill(i);
    if (bonus > 0)
    {
        getEnemy()->takeDamage(bonus);
        cout << "Additional " << bonus << " damage is dealt!" << endl;
    }
    hasbonus = (i == 1) || (hasbonus && bonus == 0);
}

void Warrior::levelup()
{
    Player::levelup();
    maxHealth += 10;
    attack += 2;
    defense += 1;
    speed += 1;
}
